//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

////////////////////////////////////////////////////////////////////////
// 
// meshMapUtils.cpp
// 
// Description:
//    Utility functions for the meshRemap and meshReorder commands
//       moveToolContext
// 
////////////////////////////////////////////////////////////////////////

#include <assert.h>

#include "meshMapUtils.h"

#include <maya/MIntArray.h>
#include <maya/MFnMesh.h>
#include <maya/MItMeshEdge.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MGlobal.h>
#include <maya/MFloatPointArray.h>
#include <maya/MDagPath.h>
#include <maya/MDagPathArray.h>
#include <maya/MObjectArray.h>
#include <maya/MItMeshVertex.h>


//
// Modulating index, will return 0..l-1, for any integer value
#define IDX(i, l) ( ( i + l ) % l )

//
// Intersect the current face list with the one provided, keep only the common elements
// 
void meshMapUtils::intersectArrays( MIntArray& selection, MIntArray& moreFaces )
{
	int j = selection.length() - 1;
	while ( j >= 0 )
	{
		bool found = false;;

		// For each item in moreFaces, try find it in fSelectedFaces
		for( unsigned int i = 0 ; i < moreFaces.length(); i++ )
		{
			if( selection[j] == moreFaces[i] )
			{
				found = true;
				break;
			}
		}	

		if( !found )
		{
			selection.remove(j);
		}

		j--;
	}
}


//
//  Recursively traverse a mesh by processing each face, and the neighbouring faces along it's edges.
//  The initial invocation of this routine provides the basis for the new first vertex/edge
//  and faces.  
//
//  The result of this routine is an array of values that map the old CV indices to the new ones. Along
//  the a new list of reindexed CVs is built, along with a list of poly counts and connetions. These
//  can be used to build a new mesh with the reordering specfied by the seed face and vertices.
//
//
//  Inputs: 
//		path				: Path to the object being traversed
//		faceIdx				: Current face being traversed
//		v0, v1				: Veretices that define the direction of travel along the face
//		faceTraversal		: An array booleans to track which faces have been 
//							: traversed, controls the recursion  
//		origVertices		: The vertices from the original mesh. The could be obtained
//							: from the path, but are passed in for efficiency
//
// 	Outputs:
//		cvMapping			: Mapping of the existing vertices to their new indices
//							: the fist values in the final array will be the intial v0, v1
//		cvMappingInverse	: The inverse of the cvMapping
//							: the value of items v0 and v1 will be 0 and 1 respectively
//		newPolygonCounts	: Vertex counts for each of the new faces
//		newPolygonConnects  : Connections, specified in terms of new CV indices
//		newVertices			: The orginal vertices resorted based on the reindexing
//
//
MStatus meshMapUtils::traverseFace( 
	MDagPath&	path,
	int faceIdx, 
	int v0, 
	int v1, 
	MIntArray& faceTraversal,
	MIntArray& cvMapping,
	MIntArray& cvMappingInverse,
	MIntArray& newPolygonCounts,
	MIntArray& newPolygonConnects,
	MFloatPointArray& origVertices,
	MFloatPointArray& newVertices
)
{
	int vtxCnt = -1;
	int dir = 0;
	int dummy;		// For setIndex calls

	MStatus stat = MStatus::kSuccess;

	MFnMesh theMesh( path, &stat );
	MItMeshPolygon polyIt( path );
	MItMeshEdge edgeIt( path );

	if( stat != MStatus::kSuccess )
	{
		MGlobal::displayError( " theMesh.getPoint failed");
		return stat;
	}


	//
	// Skip over any faces already processed, this is not a failure
	// 
	if( faceTraversal[faceIdx] )
	{
		return MStatus::kSuccess;
	}

	//
	// get the vertex/edge information and sort it based on the user seed
	//
	MIntArray vtxOrig;
	MIntArray edgeOrig;

	polyIt.setIndex( faceIdx, dummy );
	polyIt.getEdges( edgeOrig );	   
	polyIt.getVertices( vtxOrig );	   

	vtxCnt = vtxOrig.length();

	// 
	// the sorted V/E info
	// 
	MIntArray vtxSorted( vtxCnt );
	MIntArray edgeSorted( vtxCnt );

	//
	// Build a new array ordered with v0, then v1, first figure out the 
	// starting point, and direction 
	//
	int v0Idx =	-1;
	int i;
	for( i = 0; i < vtxCnt; i++ )
	{
		if( vtxOrig[i] == v0 )
		{
			// We've found v0, now find in what direction we need to travel to find v1
					
			v0Idx =	i;
			
			if( vtxOrig[IDX(i+1, vtxCnt)] == v1 )
			{
				dir = 1;
			}
			else if( vtxOrig[IDX(i-1, vtxCnt)] == v1 )
			{
				dir = -1;
			}
			else
			{
				assert( dir != 0 );
			}	
			break;
		}
	}

	// Now sort the vertex/edge arrays
	for( i = 0; i < vtxCnt; i++ )
	{
		vtxSorted[i] = vtxOrig[IDX( v0Idx + i * dir, vtxCnt )];
		if( dir == 1 )
		{
			edgeSorted[i] = edgeOrig[IDX( v0Idx + i * dir, vtxCnt )];
		}
		else
		{
			edgeSorted[i] = edgeOrig[IDX( v0Idx - 1 + i * dir, vtxCnt )];
		}
	}


	// Add any new CVs to the vertex array being constructed
	for ( i = 0; i < vtxCnt; i++ )
	{
		MPoint pos;
		int index = vtxSorted[i];

		if( cvMapping[index] == -1  )
		{
			if( stat != MStatus::kSuccess )
			{
				MGlobal::displayError( " theMesh.getPoint failed");
				return stat;
			}

			// Added the new CV, and mark it as transferred
			newVertices.append( origVertices[index] );

			// Store the mapping from the old CV indices to the new ones
			cvMapping[index] = newVertices.length()-1;
			cvMappingInverse[newVertices.length()-1] = index;
		}

	}

	//
	//  Add the new face count 
	//
	newPolygonCounts.append( vtxCnt );

	//
	//  Add the new polyConnects
	
	for ( i = 0; i < vtxCnt; i++ )
	{
		newPolygonConnects.append( cvMapping[vtxSorted[i]] );
	}

	// Mark this face as complete 
	faceTraversal[faceIdx] = true;

	//
	//  Now recurse over the edges of this face
	// 
	for( i = 0; i < (int)edgeSorted.length(); i++ )
	{
		int nextEdge = edgeSorted[i];

		int2 nextEdgeVtx;
		stat = theMesh.getEdgeVertices(nextEdge, nextEdgeVtx );

		//
		// Find the vertex, in the sorted array, that starts the next edge
		int baseIdx = -1;
		bool swap = false;
		int j;
		for( j = 0; j < (int)vtxSorted.length(); j++ )
		{
			if( vtxSorted[j] == nextEdgeVtx[0] )
			{
				baseIdx = j;
				break;
			}
		}

		assert( baseIdx != -1 );
	
		// 
		// Now look forward and backward in the vertex array to find the
		// edge's other point, this indicates the edges direction. This
		// is needed to guide the next recursion level, and keep the
		// normals pointed consistenly
		//
		if( vtxSorted[IDX(j+1, vtxCnt)] == nextEdgeVtx[1] )
		{
			// Nothing
		}
		else if ( vtxSorted[IDX(j-1, vtxCnt)] == nextEdgeVtx[1] )
		{
			swap = true;
		}


		MIntArray connectedFaces;
		edgeIt.setIndex( nextEdge, dummy );
		edgeIt.getConnectedFaces( connectedFaces );

		// A single face is simply the current one. Recurse over the others
		if( connectedFaces.length() > 1 )
		{
			int nextFace;
			if( connectedFaces[0] == faceIdx )
			{
			   	nextFace = connectedFaces[1];
			}
			else
			{
			   	nextFace = connectedFaces[0];
			}

			int nextVtx0 = -1;
			int nextVtx1 = -1;
			if ( !swap )
			{
				nextVtx0 = nextEdgeVtx[1];
				nextVtx1 = nextEdgeVtx[0];
			}
			else
			{
				nextVtx0 = nextEdgeVtx[0];
				nextVtx1 = nextEdgeVtx[1];
			}

			stat = traverseFace( path, nextFace, nextVtx0, nextVtx1, faceTraversal,
					cvMapping, cvMappingInverse, 
					newPolygonCounts, newPolygonConnects, 
					origVertices, newVertices );

			// Break out of edge loop on error
			if( stat != MStatus::kSuccess )
			{
				break;
			}	
		}
	}

	return stat;
}

//
//  Given a list of 3 paths and compoents find the face they define and store their indices
//
MStatus meshMapUtils::validateFaceSelection( MDagPathArray& paths, MObjectArray& components, int *faceIdx, MIntArray *seedVtx )
{
	MStatus stat = MStatus::kSuccess;
	MIntArray finalFaceList;

	if ( paths.length() != 3 && components.length() != 3 )
	{
		return MStatus::kFailure;
	}

	seedVtx->clear();

	for (unsigned int i = 0; i < 3; i++)
	{
		MItMeshVertex fIt ( paths[i], components[i], &stat );
		if( stat != MStatus::kSuccess || fIt.isDone() )
		{
			MGlobal::displayError( " MItMeshVertex failed");
			return MStatus::kFailure;
		}

		seedVtx->append( fIt.index() ); // The  cv's location

		MIntArray  faceList;
		stat = fIt.getConnectedFaces ( faceList );
		if( stat != MStatus::kSuccess )
		{
			MGlobal::displayError( " getConnectedFaces failed");
			return MStatus::kFailure;
		}

		if( i == 0 )
		{
			finalFaceList = faceList;
		}
		else
		{
			meshMapUtils::intersectArrays( finalFaceList, faceList );
		}
	}

	if( finalFaceList.length() != 1 )
	{
		return MStatus::kFailure;
	}
	else
	{
		*faceIdx = finalFaceList[0];
	}

	MDagPath p0( paths[0] );
	MDagPath p1( paths[1] );
	MDagPath p2( paths[2] );

	if( !(p0 == p1 ) || !(p0 == p2 ))
	{
		return MStatus::kFailure;
	}

	return MStatus::kSuccess;
}
